﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DamageCalculator.Classes
{
    public class CharacterResults
    {
        #region fields
        String mCharName;
        double mDamageDone;
        double mDPS;
        double mAvgDmg;
        double mDamageTaken;
        double mDTPS;
        double mHealed;
        double mHPS;
        double mAvgHeal;
        double mHealsTaken;
        double mHTPS;
        double mXPGain;
        double mTimeLength;
        double mTimeIdle;
        double mActionsTaken;
        double mAvgTimePerAction;
        double mCriticalHitChance;
        double mCriticalHealChance;

        Dictionary<String, double> mDamageDoneBySkill;
        Dictionary<String, double> mDamageDoneByType;
        Dictionary<String, double> mDamageDoneByTarget;

        Dictionary<String, double> mHealingDoneBySkill;
        Dictionary<String, double> mHealingDoneByTarget;

        Dictionary<String, double> mHealingTakenByCaster;
        #endregion

        #region constructors
        public CharacterResults(String Charname)
        {
            this.mCharName = Charname;
            this.mDamageDoneBySkill = new Dictionary<string, double>();
            this.mDamageDoneByType = new Dictionary<string, double>();
            this.mDamageDoneByTarget = new Dictionary<string, double>();

            this.mHealingDoneBySkill = new Dictionary<string, double>();
            this.mHealingDoneByTarget = new Dictionary<string, double>();

            this.mHealingTakenByCaster = new Dictionary<string, double>();
        }
        #endregion

        #region Properties
        public double DamageDone
        {
            get { return this.mDamageDone; }
        }

        public double DPS
        {
            get { return this.mDPS; }
        }

        public double AvgDmg
        {
            get { return this.mAvgDmg; }
        }

        public double DamageTaken
        {
            get { return this.mDamageTaken; }
        }

        public double DTPS
        {
            get { return this.mDTPS; }
        }

        public double Healed
        {
            get { return this.mHealed; }
        }

        public double HPS
        {
            get { return this.mHPS; }
        }

        public double AvgHeal
        {
            get { return this.mAvgHeal; }
        }

        public double HealsTaken
        {
            get { return this.mHealsTaken; }
        }

        public double HTPS
        {
            get { return this.mHTPS; }
        }

        public double XPGain
        {
            get { return this.mXPGain; }
        }

        public double TimeLength
        {
            get { return this.mTimeLength; }
        }

        public double TimeIdle
        {
            get { return this.mTimeIdle; }
        }

        public double ActionsTaken
        {
            get { return this.mActionsTaken; }
        }

        public double AvgTimePerAction
        {
            get { return this.mAvgTimePerAction; }
        }

        public double CriticalHitChance
        {
            get { return this.mCriticalHitChance; }
        }

        public double CriticalHealChance
        {
            get { return this.mCriticalHealChance; }
        }

        public Dictionary<String, double> DamageDonebySkill
        {
            get { return this.mDamageDoneBySkill; }
        }

        public Dictionary<String, double> DamageDonebyType
        {
            get { return this.mDamageDoneByType; }
        }

        public Dictionary<String, double> DamageDonebyTarget
        {
            get { return this.mDamageDoneByTarget; }
        }

        public Dictionary<String, double> HealingDonebySkill
        {
            get { return this.mHealingDoneBySkill; }
        }

        public Dictionary<String, double> HealingDonebyTarget
        {
            get { return this.mHealingDoneByTarget; }
        }

        public Dictionary<String, double> HealingTakenByCaster
        {
            get { return this.mHealingTakenByCaster; }
        }
        #endregion

        public void Recalculate(List<Encounter> Encounters)
        {
            this.ZeroAllValues();
            int critcount = 0;
            int hitcount = 0;
            int crithealcount = 0;
            int healcount = 0;

            foreach (Encounter E in Encounters)
            {
                //First get damage done values
                List<Entry> GivedmgEntries = E.Entries.FindAll(
                    delegate(Entry e)
                    {
                        return ((e.EventType == ParsableLogEntry.EventTypes.GiveDamage || e.EventType == ParsableLogEntry.EventTypes.CriticalGiveDamage) && e.ActiveChar == this.mCharName);
                    }
                );

                foreach (Entry e in GivedmgEntries)
                {
                    //add damage to total
                    this.mDamageDone += e.Amount;

                    //add damage by target
                    if (!this.mDamageDoneByTarget.ContainsKey(e.OtherChar))
                        this.mDamageDoneByTarget.Add(e.OtherChar, 0);
                    this.mDamageDoneByTarget[e.OtherChar] += e.Amount;

                    //add damage done by type
                    if (!this.mDamageDoneByType.ContainsKey(e.DmgType) && !String.IsNullOrEmpty(e.DmgType))
                        this.mDamageDoneByType.Add(e.DmgType, 0);

                    if (!String.IsNullOrEmpty(e.DmgType))
                        this.mDamageDoneByType[e.DmgType] += e.Amount;

                    //add damage done by skill

                    if (!this.mDamageDoneBySkill.ContainsKey(e.Skill) && !String.IsNullOrEmpty(e.Skill))
                        this.mDamageDoneBySkill.Add(e.Skill, 0);

                    if (!String.IsNullOrEmpty(e.Skill))
                        this.mDamageDoneBySkill[e.Skill] += e.Amount;

                    if (e.EventType == ParsableLogEntry.EventTypes.CriticalGiveDamage)
                        critcount++;

                    hitcount++;
                }

                //get damage taken values
                List<Entry> GetDamageEntries = E.Entries.FindAll(
                    delegate(Entry e)
                    {
                        return ((e.EventType == ParsableLogEntry.EventTypes.GetDamage || e.EventType == ParsableLogEntry.EventTypes.CriticalGetDamage) && e.ActiveChar == this.mCharName);
                    }
                );

                foreach (Entry e in GetDamageEntries)
                {
                    this.mDamageTaken += e.Amount;
                }

                //get healing done values
                List<Entry> GiveHealEntries = E.Entries.FindAll(
                    delegate(Entry e)
                    {
                        return ((e.EventType == ParsableLogEntry.EventTypes.GiveHeal || e.EventType == ParsableLogEntry.EventTypes.CriticalGiveHeal) && e.ActiveChar == this.mCharName);
                    }
                );

                foreach (Entry e in GiveHealEntries)
                {
                    this.mHealed += e.Amount;

                    if (e.EventType == ParsableLogEntry.EventTypes.CriticalGiveHeal)
                        crithealcount++;

                    healcount++;

                    if (!this.mHealingDoneBySkill.ContainsKey(e.Skill) && !String.IsNullOrEmpty(e.Skill))
                        this.mHealingDoneBySkill.Add(e.Skill, 0);

                    if (!String.IsNullOrEmpty(e.Skill))
                        this.mHealingDoneBySkill[e.Skill] += e.Amount;

                    if (!this.mHealingDoneByTarget.ContainsKey(e.OtherChar))
                        this.mHealingDoneByTarget.Add(e.OtherChar, 0);

                    this.mHealingDoneByTarget[e.OtherChar] += e.Amount;
                }

                //get healing taken data
                List<Entry> GetHealEntries = E.Entries.FindAll(
                    delegate(Entry e)
                    {
                        return ((e.EventType == ParsableLogEntry.EventTypes.CriticalGetHeal || e.EventType == ParsableLogEntry.EventTypes.CriticalGetHeal) && e.ActiveChar == this.mCharName);
                    }
                );

                foreach (Entry e in GetHealEntries)
                {
                    this.mHealsTaken += e.Amount;

                    if (!this.mHealingTakenByCaster.ContainsKey(e.OtherChar))
                        this.mHealingTakenByCaster.Add(e.OtherChar, 0);
                    this.mHealingTakenByCaster[e.OtherChar] += e.Amount;
                }

                //get xp gain data
                List<Entry> GetXPEntries = E.Entries.FindAll(
                    delegate(Entry e)
                    {
                        return e.EventType == ParsableLogEntry.EventTypes.GetXp && e.ActiveChar == this.mCharName;
                    }
                );

                foreach (Entry e in GetXPEntries)
                    this.mXPGain += e.Amount;

                //set encounter overall values
                this.mTimeLength += E.LengthInSeconds;
            }

            //calculate total encounters values
            this.mHPS = this.mHealed / this.TimeLength;
            this.mAvgHeal = this.mHealed / (double)(healcount + crithealcount);
            this.mDPS = this.mDamageDone / this.TimeLength;
            this.mAvgDmg = this.mDamageDone / (double)(hitcount + critcount);
            this.mDTPS = this.mDamageTaken / this.TimeLength;
            this.mHTPS = this.mHealsTaken / this.TimeLength;
            this.mActionsTaken = hitcount + critcount + healcount + crithealcount;
            this.mAvgTimePerAction = this.TimeLength / this.mActionsTaken;
            this.mCriticalHitChance = (double)critcount / (double)(hitcount + critcount) * 100d;
            this.mCriticalHealChance = (double)crithealcount / (double)(healcount + crithealcount) * 100d;

        }

        private void ClearDictionaries()
        {
            this.mDamageDoneBySkill.Clear();
            this.mDamageDoneByType.Clear();
            this.mDamageDoneByTarget.Clear();
            this.mHealingDoneBySkill.Clear();
            this.mHealingDoneByTarget.Clear();
            this.mHealingTakenByCaster.Clear();
        }

        private void ZeroAllValues()
        {
            mDamageDone = 0;
            mDPS = 0;
            mAvgDmg = 0;
            mDamageTaken = 0;
            mDTPS = 0;
            mHealed = 0;
            mHPS = 0;
            mAvgHeal = 0;
            mHealsTaken = 0;
            mHTPS = 0;
            mXPGain = 0;
            mTimeLength = 0;
            mTimeIdle = 0;
            mActionsTaken = 0;
            mAvgTimePerAction = 0;
            mCriticalHitChance = 0;
            mCriticalHealChance = 0;
            this.ClearDictionaries();
        }

        public List<String> generateStatisics()
        {
            List<String> temp = new List<string>();
            temp.Add("Damage Done: " + this.DamageDone.ToString("0.00"));
            temp.Add("DPS: " + this.DPS.ToString("0.00"));
            temp.Add("Average Damage: " + this.AvgDmg.ToString("0.00"));
            temp.Add("Damage Taken: " + this.DamageTaken.ToString("0.00"));
            temp.Add("DTPS: " + this.DTPS.ToString("0.00"));
            temp.Add("Total Healed: " + this.Healed.ToString("0.00"));
            temp.Add("HPS: " + this.HPS.ToString("0.00"));
            temp.Add("Average Heal: " + this.AvgHeal.ToString("0.00"));
            temp.Add("Heals Taken: " + this.HealsTaken.ToString("0.00"));
            temp.Add("HTPS: " + this.HTPS.ToString("0.00"));
            temp.Add("XPGain: " + this.XPGain.ToString("0.00"));
            temp.Add("Time Length: " + this.TimeLength.ToString("0.00"));
            temp.Add("Actions Taken: " + this.ActionsTaken.ToString("0"));
            temp.Add("Average Time/Action: " + this.AvgTimePerAction.ToString("0.00"));
            temp.Add("Critical Hit Chance: " + this.CriticalHitChance.ToString("0.00") + "%");
            temp.Add("Critical Heal Chance: " + this.CriticalHealChance.ToString("0.00") + "%");

            return temp;
        }
    }
}
